package org.stephen.recommend.model;

import com.baomidou.mybatisplus.extension.activerecord.Model;
import org.stephen.recommend.util.Utils;
import org.stephen.recommend.util.CommonUtils;

import java.time.LocalDateTime;
import java.time.temporal.ChronoUnit;
import java.util.*;
import java.util.stream.Collectors;

/**
 * ClassName: UserTag
 * Description:
 * Date: 2020/12/16 14:24
 *
 * @author stephen.qiu
 */
public class UserTags extends Model<UserTags> {
    private static final long serialVersionUID = 1L;

    private HashMap<String, Double> userTagPointMap;

    private HashMap<String, LocalDateTime> userTagTimeMap;

    public UserTags() {
        setUserTagPointMap(new HashMap<>(CommonUtils.initHashMapSize(0)));
        setUserTagTimeMap(new HashMap<>(CommonUtils.initHashMapSize(0)));
    }

    public HashMap<String, Double> getUserTagPointMap() {
        return userTagPointMap;
    }

    public void setUserTagPointMap(HashMap<String, Double> userTagPointMap) {
        this.userTagPointMap = userTagPointMap;
    }

    public HashMap<String, LocalDateTime> getUserTagTimeMap() {
        return userTagTimeMap;
    }

    public void setUserTagTimeMap(HashMap<String, LocalDateTime> userTagTimeMap) {
        this.userTagTimeMap = userTagTimeMap;
    }

    public List<String> getUserTagsList(Double declineFactor) {
        List<Map.Entry<String, Double>> list = getUserTagsListMap(declineFactor);
        return Utils.isEmpty(list) ? null : list.stream().map(c -> c.getKey()).collect(Collectors.toList());
    }

    public List<Map.Entry<String, Double>> getUserTagsListMap(Double declineFactor) {
        for (Map.Entry<String, Double> map : userTagPointMap.entrySet()) {
            long hour = ChronoUnit.HOURS.between(userTagTimeMap.get(map.getKey()), LocalDateTime.now());
            Double point = map.getValue() / Double.valueOf(String.format("%.2f", Math.exp(declineFactor * hour)));
            userTagPointMap.put(map.getKey(), point);
        }
        List<Map.Entry<String, Double>> list = new ArrayList<>(userTagPointMap.entrySet());
        Collections.sort(list, CommonUtils.getComparator());
        return list;
    }

    /**
     * @param declineFactor 标签权重衰减因子
     * @param tagMap        新tag map
     */
    public void updateMap(Double declineFactor, Map<String, Double> tagMap) {
        for (Map.Entry<String, Double> tag : tagMap.entrySet()) {
            Double point;
            if (userTagPointMap.containsKey(tag.getKey())) {
                /**
                 * 标签热度衰减
                 */
                long hour = ChronoUnit.HOURS.between(userTagTimeMap.get(tag.getKey()), LocalDateTime.now());
                point = tag.getValue() / Double.valueOf(String.format("%.2f", Math.exp(declineFactor * hour))) + userTagPointMap.get(tag.getKey());
            } else {
                point = tag.getValue();
            }
            userTagPointMap.put(tag.getKey(), point);
            userTagTimeMap.put(tag.getKey(), LocalDateTime.now());
        }
    }

    /**
     * 初始化 userTagTimeMap和 userTagPointMap
     *
     * @param tagMap
     */
    public void createUserTags(HashMap<String, Double> tagMap) {
        setUserTagPointMap(tagMap);
        for (Map.Entry<String, Double> tag : tagMap.entrySet()) {
            userTagTimeMap.put(tag.getKey(), LocalDateTime.now());
        }
    }

    /**
     * 移除previousDays内未更新的用户标签
     *
     * @param previousDays
     */
    public void updateUserTag(int previousDays) {
        Iterator<Map.Entry<String, LocalDateTime>> iterator = userTagTimeMap.entrySet().iterator();
        while (iterator.hasNext()) {
            Map.Entry<String, LocalDateTime> entry = iterator.next();
            if (entry.getValue().isBefore(LocalDateTime.now().plusDays(0 - previousDays))) {
                iterator.remove();
                userTagPointMap.remove(entry.getKey());
            }
        }
    }
}